import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 注意点！注意点！注意点！
 * 1、使用此方法之前 需要先切换分支   比如切换到 master 分支
 * 2、第一次使用后 建议执行完程序后，自己检查下依赖是否都正确！！！！
 * 3、对于json文件中的“当前项目”的版本  要先填写升级版本
 * 比如当前项目A的 realease 版本为         1.0.0    develop版本为  1.0.0-SNAPSHOT
 * 那么就需要在json文件中指定 realease为   1.0.1    develop版本为  1.0.2-SNAPSHOT
 */
public class CmdRunner {

    private static final Pattern pattern = Pattern.compile("Dependency '((.*):(.*))' is a snapshot");

    private volatile static String curDependencyName = "";

    private volatile static String curMessage = "";

    private static String tagLogFilePath = System.getProperty("user.dir") + File.separator + "log" + File.separator + "info.log";

    private static final String cmdConfirm = "\r\n";

    private static final String enterCharacter = "\r";

    private static final String newlineCharacter = "\n";

    private static final String cmdEndCharacter = ": : ";

    private static final String apiSplit = "、";

    private volatile static ConcurrentHashMap<String, List<String>> apiVersionMap;

    private volatile static AtomicInteger count = new AtomicInteger(0);

    private volatile static String curProjectName;

    private volatile static AtomicBoolean isEnd = new AtomicBoolean(false);

    private volatile static AtomicBoolean isStart = new AtomicBoolean(false);

    private volatile static AtomicBoolean resultIsError = new AtomicBoolean(false);

    public static void main(String[] args) {

        FileUtils.delFile(tagLogFilePath);

        FileUtils.createOrExistsFile(new File(tagLogFilePath));

        if (args.length < 5) {

            throw new IllegalArgumentException("- - - - - - - - - - 参数有误，请检查参数- - - - - - - - - ");

        }
        String userName = args[0];

        String userPassword = args[1];

        String projectRootDir = args[2];

        String jsonFilePath = args[3];

        curProjectName = args[4];

        apiVersionMap = GsonUtils.fromJson(new File(jsonFilePath), ConcurrentHashMap.class);

        apiVersionMap.forEach((k, v) -> {

            if (k.contains(apiSplit)) {
                String[] apiNameList = k.split(apiSplit);
                for (String apiName : apiNameList) {
                    apiVersionMap.put(apiName, v);
                }
                apiVersionMap.remove(k);
            }
        });

        try {
            /**
             *  cmd /c  mvn release:clean release:prepare -Dusername=xxc -Dpassword=123456 release:perform -Dusername=xxc -Dpassword=123456
             */
            StringBuilder sb = new StringBuilder();
            sb.append("cmd /c  ");
            sb.append("mvn release:clean release:prepare -Dusername=");
            sb.append(userName);
            sb.append(" -Dpassword=");
            sb.append(userPassword);
            sb.append(" release:perform -Dusername=");
            sb.append(userName);
            sb.append(" -Dpassword=");
            sb.append(userPassword);

            String cmd = sb.toString();
            Process process = Runtime.getRuntime().exec(cmd, null, new File(projectRootDir));
            BufferedOutputStream out = new BufferedOutputStream(process.getOutputStream());
            BufferedInputStream in = new BufferedInputStream(process.getInputStream());
            BufferedInputStream error = new BufferedInputStream(process.getErrorStream());

            /**
             * 输出流线程
             */
            Thread readInputStreamThread = new Thread(() -> {

                handle(process, in, msg -> {
                    isStart.compareAndSet(false, true);
                    System.out.println("【提示信息】 " + msg);
                    curMessage = msg;
                    handleMessage(msg, out, args);
                });

            });

            /**
             * 错误流线程
             */
            Thread readErrorStreamThread = new Thread(() -> {

                handle(process, error, msg -> {

                    System.err.println("【读取错误信息】：" + msg);

                });

            });

            /**
             * 监控线程 防止程序卡死
             */
            Thread monitoringThread = new Thread(() -> {

                while (true) {

                    //开始 并且 填写依赖未结束
                    if (isStart.get() && !isEnd.get()) {
                        try {
                            String preStage = curDependencyName;

                            Thread.sleep(3000);
                            //睡眠3s以后发现当前阶段和上一次相等 并且 不等于当前项目依赖
                            if (Objects.equals(preStage, curDependencyName) && !Objects.equals(preStage, curProjectName)) {
                                System.err.println("==================================================================");
                                System.err.println("=====================程序假死，正在补救===========================");
                                System.err.println("==================================================================");
                                handleMessage(curMessage, out, args);
                            }

                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                }
            });

            readInputStreamThread.setDaemon(true);
            readErrorStreamThread.setDaemon(true);
            monitoringThread.setDaemon(true);

            readInputStreamThread.start();
            readErrorStreamThread.start();
            monitoringThread.start();
            process.waitFor();

            System.out.println("- - - - - - - - - - 【程序已执行完毕，执行结果：【" + (resultIsError.get() ? "失败" : "成功") + "】 具体信息请看 【" + tagLogFilePath + "】 文件中的信息！】- - - - - - - - - -");

            if (Objects.nonNull(process)) {
                process.destroy();
                FileUtils.closeIO(in, out, error);
                isEnd.set(true);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 封装从管道中读取的消息处理
     *
     * @param msg
     * @param out
     * @param args
     */
    private static void handleMessage(String msg, OutputStream out, String[] args) {

        if (msg.contains("Do you want to resolve them now")) {
            writeCmdToOutStream("yes", out);
        } else if (msg.contains(curProjectName)) {
            count.incrementAndGet();
            String dependencyVersion = null;
            if (msg.contains("release") || msg.contains("tag")) {
                dependencyVersion = getCurDependencyVersion(args.length > 5 ? args[5] : null, () -> {
                    return getApiReleaseVersion();
                });
                writeCmdToOutStream(dependencyVersion, out);
            } else if (msg.contains("development")) {
                dependencyVersion = getCurDependencyVersion(args.length > 6 ? args[6] : null, () -> {
                    return getApiDevelopVersion();
                });
                writeCmdToOutStream(dependencyVersion, out);
            }
            System.out.println("【当前版本操作信息：】" + msg + dependencyVersion);

        } else if (msg.contains("specify the selection number")) {
            writeCmdToOutStream("1", out);
        } else if (msg.contains("Which release version should it be set to")) {
            String testVersion = getApiReleaseVersion();
            System.out.println("【操作信息】 Which " + curDependencyName + " release version should it be set to " + testVersion);
            writeCmdToOutStream(testVersion, out);
        } else if (msg.contains("What version should the dependency be reset to for development")) {
            String devVersion = getApiDevelopVersion();
            System.out.println("【操作信息】 What " + curDependencyName + " version should the dependency be reset to for development " + devVersion);
            writeCmdToOutStream(devVersion, out);
        }

    }

    /**
     * 根据api 版本名称 获取 realease 版本
     *
     * @return
     */
    public static String getApiReleaseVersion() {

        if (Objects.isNull(curDependencyName)) {
            return null;
        }
        for (Map.Entry<String, List<String>> entry : apiVersionMap.entrySet()) {
            if (entry.getKey().contains(curDependencyName)) {
                return entry.getValue().get(0);
            }
        }
        return null;
    }

    /**
     * 根据api 版本名称 获取 develop 版本
     */
    public static String getApiDevelopVersion() {

        if (Objects.isNull(curDependencyName)) {
            return null;
        }

        for (Map.Entry<String, List<String>> entry : apiVersionMap.entrySet()) {
            if (entry.getKey().contains(curDependencyName)) {
                return entry.getValue().get(1);
            }
        }
        return null;
    }

    /**
     * 像管道中写入命令
     *
     * @param cmd
     * @param ous
     */
    public static void writeCmdToOutStream(String cmd, OutputStream ous) {
        try {
            if (Objects.isNull(cmd) || Objects.equals("", cmd)) {
                throw new IllegalArgumentException("- - - - - - - - - - cmd is not null - - - - - - - - - - ");
            }
            ous.write(cmd.getBytes());
            if (!Objects.equals(cmdConfirm, cmd)) {
                ous.write(cmdConfirm.getBytes());
            }
            ous.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 核心处理函数
     *
     * @param process
     * @param ins
     * @param callBcak
     */
    public static void handle(Process process, InputStream ins, CallBackFunction callBcak) {
        int length = 0;
        try {
            while (true) {
                byte[] buffer = new byte[ins.available()];
                if ((length = ins.read(buffer)) != -1) {
                    String content = new String(buffer, "GBK");
                    if (count.get() >= 3 && !"".equals(content)) {
                        //追加写到文件里
                        if (content.contains("ERROR")) {
                            resultIsError.compareAndSet(false,true);
                        }
                        FileUtils.write(new File(tagLogFilePath), new ByteArrayInputStream(buffer), true);
                    } else if ((!content.contains(newlineCharacter) && !content.contains(enterCharacter)) && content.contains(cmdEndCharacter)) {
                        callBcak.handle(content);
                    } else if (content.contains("Dependency") && content.contains("is a snapshot")) {
                        String matchResult = getMatchResult(content);
                        apiVersionMap.keySet().forEach((key) -> {
                            if (matchResult.contains(key)) {
                                curDependencyName = key;
                            }
                        });
                        callBcak.handle(content);
                    }
                }
            }
        } catch (IOException e) {
        }
    }


    /**
     * 正则表达式匹配 需要更改的api 版本
     *
     * @param msg
     * @return
     */
    public static String getMatchResult(String msg) {

        Matcher matcher = pattern.matcher(msg);
        boolean isSuccess = matcher.find();
        if (isSuccess) {
            return matcher.group(3);
        }
        return null;
    }


    /**
     * @param DependencyName
     * @param callBack
     * @return
     */
    public static String getCurDependencyVersion(String DependencyName, BaseCallBack callBack) {

        String result;
        if (Objects.isNull(DependencyName)) {
            curDependencyName = curProjectName;
            result = callBack.handle();
            if (Objects.isNull(result)) {
                return cmdConfirm;
            } else {
                return result;
            }
        } else {
            return DependencyName;
        }
    }


}
